﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Scanner
{
    class Scanner
    {
        private string source;
        private Token nextToken;
        private int sourcePosition;

        public Scanner()
        {
            this.sourcePosition = 0;
        }

        public void Init(string source)
        {
            this.source = source;
            this.sourcePosition = 0;
            nextToken = Scan();
        }

        public Token Next()
        {
            Token currToken = this.nextToken;
            while (true)
            {
                this.nextToken = Scan();
                if (this.nextToken.getType() != TokenKind.Illegal)
                {
                    break;
                }
            }

            return currToken;
        }

        public Token Peek()
        {
            return this.nextToken;
        }

        private Token Scan()
        {
            SkipWhiteSpaces();

            char ch = ReadChar();
            switch (ch)
            {
                case '&':
                case '|':
                    if (PeekChar() == ch)
                    {
                        ReadChar();
                        return new Token(TokenKind.LogOper, "" + ch + ch);
                    }
                    break;

                case '!':
                    if (PeekChar() == '=')
                    {
                        ReadChar();
                        string value = "!=";

                        if (PeekChar() == '=')
                        {
                            value += ReadChar();
                        }
                        return new Token(TokenKind.CompOper, value);
                    }
                    break;

                case '=':
                    if (PeekChar() == '=')
                    {
                        ReadChar();
                        string value = "==";

                        if (PeekChar() == '=')
                        {
                            value += ReadChar();
                        }
                        return new Token(TokenKind.CompOper, value);
                    }
                    break;

                case '<':
                case '>':
                    string val = ch.ToString();
                    if (PeekChar() == '=')
                    {
                        val += ReadChar();
                    }
                    break;

                default:
                    if (IsIdentifierBegin(ch))
                    {
                        return ReadIdentifier(ch);
                    }
                    break;
            }
            return GetSimpleToken(ch);
        }

        private Token GetSimpleToken(char ch)
        {
            TokenKind type;
            switch (ch)
            {
                case (char)0:
                    type = TokenKind.Eos;
                    break;

                case '+':
                case '-':
                case '*':
                case '/':
                    type = TokenKind.ArifOper;
                    break;

                case '!':
                    type = TokenKind.LogOper;
                    break;

                case '=':
                    type = TokenKind.Assign;
                    break;

                case '<':
                case '>':
                    type = TokenKind.CompOper;
                    break;

                case '(':
                    type = TokenKind.LeftBracket;
                    break;

                case '{':
                    type = TokenKind.LeftBrace;
                    break;

                case ')':
                    type = TokenKind.RightBracket;
                    break;

                case '}':
                    type = TokenKind.RightBrace;
                    break;

                default:
                    type = TokenKind.Illegal;
                    break;
            }

            return new Token(type, ch.ToString());
        }

        private Token ReadIdentifier(char ch)
        {
            string value = ch.ToString();
            for (; IsIdentifierPart(ch = PeekChar()); ReadChar())
            {
                value += ch;
            }

            return new Token(TokenKind.Id, value);
        }

        private bool IsIdentifierBegin(char ch)
        {
            return (ch == '_' || Char.IsLetter(ch));
        }

        private bool IsIdentifierPart(char ch)
        {
            return (IsIdentifierBegin(ch) || Char.IsDigit(ch));
        }

        private void SkipWhiteSpaces()
        {
            for (; Char.IsWhiteSpace(PeekChar()); ReadChar());
        }

        private char PeekChar()
        {
            if (sourcePosition < source.Length)
            {
                return source[sourcePosition];
            }
            else
            {
                return (char)0;
            }
        }

        private char ReadChar()
        {
            char ch = PeekChar();
            if (ch != 0)
            {
                ++sourcePosition;
            }

            return ch;
        }
    }
}
